package com.cute.leetcode.editor.cn.teacher.zs.class03;

/**
 *  归并排序
 */
public class Code01_MergeSort {



	// 递归方法实现
	public static void mergeSort1(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		process(arr, 0, arr.length - 1);
	}

	// arr[L...R]范围上，变成有序的
	// L...R    N    T(N) = 2*T(N/2) + O(N)  ->
	public static void process(int[] arr, int L, int R) {

		// L==R 说明 arr 上面只有一个数
		if (L == R) { // base case
			return;
		}
		int mid = L + ((R - L) >> 1);
		process(arr, L, mid);
		process(arr, mid + 1, R);
		merge(arr, L, mid, R);
	}

	public static void merge(int[] arr, int L, int M, int R) {
		// R-L+1  L 到R 有多少个数 数组就准备多长
		int[] help = new int[R - L + 1];
		int i = 0;
		int p1 = L;
		int p2 = M + 1;

		// p1<=M  p1 不能越界  p2<=R   p2 不能越界
		while (p1 <= M && p2 <= R) {
			// 如果 p1 位置的数小于等于p2位置的数,则把p1位置的数拷贝到help[i] 位置来;
			// 同时p1指针右移,i++;
			help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
		}
		// 要么p1越界了，要么p2越界了
		while (p1 <= M) {
			// p2 越界了 把p1指针右边的拷贝下来
			help[i++] = arr[p1++];
		}
		while (p2 <= R) {
			// p1 越界了 把p2指针右边的拷贝下来
			help[i++] = arr[p2++];
		}
		for (i = 0; i < help.length; i++) {
			arr[L + i] = help[i];
		}
	}

	// 非递归方法实现
	public static void mergeSort2(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		int N = arr.length;
		// 步长
		int step = 1;// 当前有序的，左组长度
		// 步长小于总长度结束
		while (step < N) { // log N
			int L = 0;
			// 0....
			// L<N 是为了避免越界
			while (L < N) {
				// L...M  左组（step）
				// M 是左组的最后一个位置
//				int M = L + step - 1;
				//~~~~~~~~~~~~~~~~~
				int M=0;
				// L...N-1   中间的数据个数是 N-L  如果剩下的数 大于等于步长,说明能够凑够一组
				if(N-L>=step){
					M=L+step-1;
				}else{
					// 剩下的数据小于步长,说明剩下的数据凑不够一组,M 只能等于N-1
					M=N-1;
				}
				//L....M
				if(M==N-1){
					break;
				}
				//~~~~~~~~~~~~~~~~~

				/*if (M >= N) {
					break;
				}*/
				//  L...M   M+1...R(step)
//				int R = Math.min(M + step, N - 1);
				//~~~~~~~~~~~~~~~~~
				int R=0;
				if(N-1-M>=step){
					R=M+step;
				}else {
					R=N-1;
				}
				// L...M  M+1...R  L 到M 就是左组  M+1 到R 就是右组
				//~~~~~~~~~~~~~~~~~

				merge(arr, L, M, R);
//				L = R + 1;
				if(R==N-1){
					break;
				}else {
					L = R + 1;
				}
			}
			// 这个是为了避免步长超过临界值 Integer.Max  因为步长是 1=》2=》4=》8...  所以要判断步长是否大于N/2
			if (step > N / 2) {
				break;
			}
			// 步长乘以2
			step <<= 1;
		}
	}

	// for test
	public static int[] generateRandomArray(int maxSize, int maxValue) {
		int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
		}
		return arr;
	}

	// for test
	public static int[] copyArray(int[] arr) {
		if (arr == null) {
			return null;
		}
		int[] res = new int[arr.length];
		for (int i = 0; i < arr.length; i++) {
			res[i] = arr[i];
		}
		return res;
	}

	// for test
	public static boolean isEqual(int[] arr1, int[] arr2) {
		if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
			return false;
		}
		if (arr1 == null && arr2 == null) {
			return true;
		}
		if (arr1.length != arr2.length) {
			return false;
		}
		for (int i = 0; i < arr1.length; i++) {
			if (arr1[i] != arr2[i]) {
				return false;
			}
		}
		return true;
	}

	// for test
	public static void printArray(int[] arr) {
		if (arr == null) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	// for test
	public static void main(String[] args) {
		int testTime = 500000;
		int maxSize = 100;
		int maxValue = 100;
		boolean succeed = true;
		for (int i = 0; i < testTime; i++) {
			int[] arr1 = generateRandomArray(maxSize, maxValue);
			int[] arr2 = copyArray(arr1);
			mergeSort1(arr1);
			mergeSort2(arr2);
			if (!isEqual(arr1, arr2)) {
				succeed = false;
				printArray(arr1);
				printArray(arr2);
				break;
			}
		}
		System.out.println(succeed ? "Nice!" : "Oops!");
	}

}
